#include "memory.h"
#include "bitmap.h"
#include "stdint.h"
#include "global.h"
#include "debug.h"
#include "print.h"
#include "string.h"
#include "sync.h"
#include "interrupt.h"
#include "console.h"

#define PG_SIZE 4096

// ================ 位图地址 ================
// 为什么是 0xc009a000?
// 因为 0xc009f000 - 0x1000 - 0x1 = 0xc009e000 这是 PCB 表尾
// 0xc009e000 是将来主线程存放 PCB 的地址（表头 + 4k 为 0xc009efff）
// 因为页单位为 4k，位图以字节为单位划分，细粒度单位是位，每位表示 4k 即一页
// 故而一页大小的位图可管理的内存容量：4k * 8 * 4k = 128M
// 当前我们给虚拟机分配了 32Mb 内存空间，因此这才用了四分之一页
// 为了扩展，本书中这里假设要管理 4页 的位图，即最大可管理 512MB 的物理内存
// 综上所述，0xc009e000 还需要减去 4页 的位图：0xc009e000 - 0x4000 = 0xc009a000
#define MEM_BITMAP_BASE 0xc009a000
// =========================================

#define PDT_IDX(addr) ((addr & 0xffc00000) >> 22) // 获取 addr 的高 10 位
#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) // 获取 addr 的中间 10 位

/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存,使虚拟地址在逻辑上连续 */
#define K_HEAP_START 0xc0100000

// 内存池结构
struct pool {
    struct bitmap pool_bitmap; // 该内存池所用到的位图结构，用于管理物理内存
    uint32_t phy_addr_start;   // 该内存池所管理物理内存的起始地址
    uint32_t pool_size;        // 该内存池字节容量
    struct lock lock;          // 申请内存时互斥
};

// 内存仓库 arena 的元信息
struct arena {
    struct mem_block_desc* desc; // 此 arena 锁关联的 mem_block_desc
    uint32_t cnt;                // 若 large = true，则 cnt 表示页宽数
                                 // 若 large = false，则 cnt 表示空闲 mem_block 的数量
    bool large; // 标识该 arena 到底是管理 1024 字节内的内存(true)，如何超过 1024 字节的内存(false)
};

struct mem_block_desc k_block_descs[DESC_CNT]; // 内核内存块描述符数组
struct pool kernel_pool, user_pool; // 内核物理内存池和用户物理内存池
struct virtual_addr kernel_vaddr;   // 用于给内核分配虚拟地址

// 在 pf 所表示的内存池中申请 pg_cnt 个虚拟页
// 成功时返回虚拟页的起始地址，失败则返回 NULL
static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) {
    int vaddr_start = 0, bit_idx_start = -1;
    uint32_t cnt = 0;
    if(pf == PF_KERNEL) { // 内核内存池
        bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt); // 扫描是否有足够的空间
        if(bit_idx_start == -1) { // 没有足够的空间
            return NULL;
        }
        while(cnt < pg_cnt) { // 有足够的空间，则将这 pg_cnt 个页的状态设置为 1 表示已占用
            bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
        }

        // 返回的虚拟地址：内核虚拟内存池的起始地址 + 相对于内核内存池的偏移量 bit_idx_start * PG_SIZE(每位代表一页)
        vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
    } else { // 用户内存池
        struct task_struct* cur = running_thread();
        bit_idx_start = bitmap_scan(&cur -> userprog_vaddr.vaddr_bitmap, pg_cnt);

        // 未找到可分配空间
        if(bit_idx_start == -1) return NULL;

        while(cnt < pg_cnt) // 将这 pg_cnt 个位的位图置为 1
            bitmap_set(&cur -> userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
        
        vaddr_start = cur -> userprog_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
        ASSERT((uint32_t) vaddr_start < (0xc0000000 - PG_SIZE));
    }
    return (void*) vaddr_start;
}

// 得到虚拟地址 vaddr 对应的 PTE 指针
uint32_t* pte_ptr(uint32_t vaddr) {
    // 访问页目录的最后一个 PDE，将当前页目录表当成页表
    // 利用 vaddr 的高 10 位，去找到真正的页表的物理地址（按处理器的思想，这步得到的应该是物理页的物理地址，但这里我们将其看成真正的页表物理地址）
    // 利用 vaddr 的中间 10 位，作为页表的偏移量，从而找到 vaddr 对应的 PTE
    uint32_t* pte = (uint32_t*)(0xffc00000 + \
    ((vaddr & 0xffc00000) >> 10) + \
    PTE_IDX(vaddr) * 4); 
    return pte;
}

// 得到虚拟地址 vaddr 对应的 PDT 指针
uint32_t* pde_ptr(uint32_t vaddr) {
    uint32_t* pde = (uint32_t*) ((0xfffff000) + PDT_IDX(vaddr) * 4);
    return pde;
}

// 在 m_pool 指向的物理内存池中分配一个物理页
// 返回页框的物理地址，否则返回 NULL
static void* palloc(struct pool* m_pool) {
    // 扫描或设置位图要保证原子操作
    int bit_idx = bitmap_scan(&m_pool -> pool_bitmap, 1); // 找到一个物理页
    if(bit_idx == -1) return NULL;
    bitmap_set(&m_pool -> pool_bitmap, bit_idx, 1); // 设置该位使用状态为：已使用
    uint32_t page_phyaddr = ((bit_idx * PG_SIZE) + m_pool -> phy_addr_start);
    return (void*) page_phyaddr;
}

// 页表中添加虚拟地址 _vaddr 与物理地址 _page_phyaddr 的映射
static void page_table_add(void* _vaddr, void* _page_phyaddr) {
    uint32_t vaddr = (uint32_t) _vaddr, page_phyaddr = (uint32_t) _page_phyaddr;
    uint32_t* pde = pde_ptr(vaddr);
    uint32_t* pte = pte_ptr(vaddr);

    // 判断页目录项中的 P 位是否为 1，即判断 PDE 是否存在
    if(*pde & 0x00000001) { // 存在
        ASSERT(!(*pte & 0x00000001));
        // 判断 PTE 是否存在
        if(!(*pte & 0x00000001)) { // 不存在
            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
        } else {
            // 这个分支，我个人感觉压根没存在的必要
            // 因为 PTE 是不存在才创建，而这个分支表示已存在，那还创建个屁？
            PANIC("pte repeat");
            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
        }
    } else { // 不存在
        // 页表中用到的页框一律从内核内存池分配
        // PDE 不存在，自然 PTE 页不存在则需要创建一个新的页表
        uint32_t pde_phyaddr = (uint32_t) palloc(&kernel_pool);

        *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);

        memset((void*)((int)pte & 0xfffff000), 0, PG_SIZE); // 避免脏读

        ASSERT(!(*pte & 0x00000001));

        *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
    }
}

// 分配 pg_cnt 个页空间，返回虚拟地址，否则 NULL
void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) {
    ASSERT(pg_cnt > 0 && pg_cnt < 3840);
    void* vaddr_start = vaddr_get(pf, pg_cnt);
    if(vaddr_start == NULL) return NULL;
    uint32_t vaddr = (uint32_t) vaddr_start, cnt = pg_cnt;
    struct pool* mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;

    // 因为虚拟地址是连续的，但物理地址可以是不连续的，所以要逐个做映射
    while(cnt-- > 0) {
        void* page_phyaddr = palloc(mem_pool);
        if(page_phyaddr == NULL) { // 若其中一块分配失败，则回滚之前分配的空间
            // 待实现...
            return NULL;
        }
        page_table_add((void*) vaddr, page_phyaddr); // 在页表中做映射
        vaddr += PG_SIZE; // 下一个虚拟页
    }
    return vaddr_start;
}

// 从内核物理内存池中申请 pg_cnt 页内存，返回虚拟地址，否则返回 NULL
void* get_kernel_pages(uint32_t pg_cnt) {
    lock_acquire(&kernel_pool.lock);
    void* vaddr = malloc_page(PF_KERNEL, pg_cnt);
    if(vaddr != NULL) { // 若分配的地址不为空，先将页框清 0，避免脏读
        memset(vaddr, 0, pg_cnt * PG_SIZE);
    }
    lock_release(&kernel_pool.lock);
    return vaddr;
}

// 在用户物理内存池中申请 pg_cnt 页内存，返回虚拟地址
void* get_user_pages(uint32_t pg_cnt) {
    lock_acquire(&user_pool.lock);
    void* vaddr = malloc_page(PF_USER, pg_cnt);
    memset(vaddr, 0, pg_cnt * PG_SIZE);
    lock_release(&user_pool.lock);
    return vaddr;
}

// 将地址 vaddr 与 pf 池中的物理地址关联，仅支持一页空间分配
// vaddr 是自定义虚拟地址，而 get_*_pages() 是系统分配的，不是我们指定的
void* get_a_page(enum pool_flags pf, uint32_t vaddr) {
    struct pool* mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;
    lock_acquire(&mem_pool -> lock);

    // 先将虚拟地址对应的位图置为 1
    struct task_struct* cur = running_thread();
    int32_t bit_idx = -1;

    // 若当前是用户进程申请用户内存，就修改用户进程自己的虚拟地址位图
    if(cur -> pgdir != NULL && pf == PF_USER) {
        bit_idx = (vaddr - cur -> userprog_vaddr.vaddr_start) / PG_SIZE;
        ASSERT(bit_idx > 0);
        bitmap_set(&cur -> userprog_vaddr.vaddr_bitmap, bit_idx, 1);
    } else if(cur -> pgdir == NULL && pf == PF_KERNEL) {
        // 若是内核线程申请内存，就修改 kernel_vaddr
        bit_idx = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE;
        ASSERT(bit_idx > 0);
        bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx, 1);
    } else {
        PANIC("get_a_page:not allow kernel alloc userspace or user alloc kernelspace by get_a_page.");
    }

    void* page_phyaddr = palloc(mem_pool);
    if(page_phyaddr == NULL) return NULL;
    page_table_add((void*) vaddr, page_phyaddr);
    lock_release(&mem_pool -> lock);
    return (void*) vaddr;
}

// 得到虚拟地址映射到物理地址
uint32_t addr_v2p(uint32_t vaddr) {
    uint32_t* pte = pte_ptr(vaddr);
    // *PTE 的值是页表所在的物理页框地址
    // 去掉其低 12 位的页表项属性 + 虚拟地址 vaddr 的低 12 位
    return ((*pte & 0xFFFFF000) + (vaddr & 0x00000FFF));
}

// 返回 arena 中第 idx 个内存块的地址
static struct mem_block* arena2block(struct arena* a, uint32_t idx) {
    return (struct mem_block*) ((uint32_t)a + sizeof(struct arena) + idx * a -> desc -> block_size);
}

// 返回内存块 b 所在的 arena 地址
static struct arena* block2arena(struct mem_block* b) {
    return (struct arena*)((uint32_t)b & 0xFFFFF000);
}

// 在堆中申请 size 字节内存
void* sys_malloc(uint32_t size) {
    enum pool_flags PF;
    struct pool* mem_pool;
    uint32_t pool_size;
    struct mem_block_desc* descs;
    struct task_struct* cur_thread = running_thread();

    // 判断使用哪个内存池
    if(cur_thread -> pgdir == NULL) { // 内核线程
        PF = PF_KERNEL;
        pool_size = kernel_pool.pool_size;
        mem_pool = &kernel_pool;
        descs = k_block_descs;
    } else { // 用户线程
        PF = PF_USER;
        pool_size = user_pool.pool_size;
        mem_pool = &user_pool;
        descs = cur_thread -> u_block_desc;
    }

    // 若申请的内存不在内存池容量范围内，则直接返回 NULL
    if(!(size > 0 && size < pool_size)) return NULL;

    struct arena* a;
    struct mem_block* b;
    lock_acquire(&mem_pool -> lock);

    // 超过最大内存块 1024 字节，就分配页框
    if(size > 1024) {
        uint32_t page_cnt = DIV_ROUND_UP(size + sizeof(struct arena), PG_SIZE);

        a = malloc_page(PF, page_cnt);

        if(a == NULL) {
            lock_release(&mem_pool -> lock);
            return NULL;
        }

        memset(a, 0, page_cnt * PG_SIZE);

        // 对于分配的大块页框，要初始化如下：
        a -> desc = NULL;    // 没有关联的内存块描述符
        a -> cnt = page_cnt; // 页框的数量
        a -> large = true;   // 该 arena 表示为页框

        lock_release(&mem_pool -> lock);
        return (void*) (a + 1); // 跨过 arena 大小，把剩下的内存将其首地址返回
    } else {
        uint8_t desc_idx;

        // 从内存块描述符中匹配合适的内存块规格
        for(desc_idx = 0; desc_idx < DESC_CNT; desc_idx++) {
            if(size <= descs[desc_idx].block_size) break;
        }

        if(list_empty(&descs[desc_idx].free_list)) {
            a = malloc_page(PF, 1);
            if(a == NULL) {
                lock_release(&mem_pool -> lock);
                return NULL;
            }
            memset(a, 0, PG_SIZE);

            a -> desc = &descs[desc_idx];
            a -> cnt = descs[desc_idx].blocks_per_arena;
            a -> large = false;

            uint32_t block_idx;

            enum intr_status old_status = intr_disable();

            // 开始将 arena 拆分成内存块，并添加到内存块描述符的 free_list 中
            for(block_idx = 0; block_idx < descs[desc_idx].blocks_per_arena; block_idx++) {
                b = arena2block(a, block_idx);
                ASSERT(!elem_find(&a -> desc -> free_list, &b -> free_elem));
                list_append(&a -> desc -> free_list, &b -> free_elem);
            }

            intr_set_status(old_status);
        }

        // 开始分配内存块
        b = elem2entry(struct mem_block, free_elem, list_pop(&(descs[desc_idx].free_list)));
        memset(b, 0, descs[desc_idx].block_size);

        a = block2arena(b); // 获取内存块 b 所在的 arena
        a -> cnt--;         // 将此 arena 中的空闲内存块减一
        lock_release(&mem_pool -> lock);
        return (void*) b;
    }
}

// 将物理地址 pg_phy_addr 回收到物理内存池
void pfree(uint32_t pg_phy_addr) {
    struct pool* mem_pool;
    uint32_t bit_idx = 0;
    if(pg_phy_addr >= user_pool.phy_addr_start) { // 用户物理内存池
        mem_pool = &user_pool;
        bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE;
    } else { // 内核物理内存池
        mem_pool = &kernel_pool;
        bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE;
    }
    bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0);
}

// 去掉页表中虚拟地址 vaddr 的映射，只去掉 vaddr 对应的 PTE
static void page_table_pte_remove(uint32_t vaddr) {
    uint32_t* pte = pte_ptr(vaddr);
    *pte &= ~PG_P_1; // 将页表项 PTE 的 P 位置为 0
    asm volatile("invlpg %0" : : "m"(vaddr) : "memory"); // 更新 TLB
}

// 在虚拟地址内存池中释放以 _vaddr 起始的连续 pg_cnt 个虚拟页地址
static void vaddr_remove(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt) {
    uint32_t bit_idx_start = 0, vaddr = (uint32_t) _vaddr, cnt = 0;
    if(pf == PF_KERNEL) { // 内核虚拟内存池
        bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE; // 计算 vaddr 在虚拟内存池中的偏移量
        while(cnt < pg_cnt) 
            bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0);
    } else { // 用户虚拟内存池
        struct task_struct* cur_thread = running_thread();
        bit_idx_start = (vaddr - cur_thread -> userprog_vaddr.vaddr_start) / PG_SIZE;
        while(cnt < pg_cnt)
            bitmap_set(&cur_thread -> userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0);
    }
}

// 释放以虚拟地址 vaddr 为起始的 cnt 个物理页信息
void mfree_page(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt) {
    uint32_t pg_phy_addr;
    uint32_t vaddr = (int32_t) _vaddr, page_cnt = 0;
    ASSERT(pg_cnt >= 1 && vaddr % PG_SIZE == 0);
    pg_phy_addr = addr_v2p(vaddr);

    // 确保待释放的物理内存 存在低端 1M + 1k 大小的页目录 + 1k 大小的页表地址范围外
    ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= 0x102000);

    // 判断 pg_phy_addr 属于哪个区域
    if(pg_phy_addr >= user_pool.phy_addr_start) { // 用户物理内存池
        vaddr -= PG_SIZE;
        while(page_cnt < pg_cnt) {
            vaddr += PG_SIZE;
            pg_phy_addr = addr_v2p(vaddr);

            // 确保物理地址属于用户内存池
            ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= user_pool.phy_addr_start);

            // 先将对应的物理页框规划到内存池
            pfree(pg_phy_addr);

            // 再从页表中清除此虚拟地址所在页表项 PTE
            page_table_pte_remove(vaddr);

            //pg_cnt++;
            page_cnt++;
        }
        // 清空虚拟地址位图中相应的位
        vaddr_remove(pf, _vaddr, pg_cnt);
    } else { // 内核物理内存池
        vaddr -= PG_SIZE;
        while(page_cnt < pg_cnt) {
            vaddr += PG_SIZE;
            pg_phy_addr = addr_v2p(vaddr);
            // 确保待释放的物理内存只属于内核物理内存池
            ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= kernel_pool.phy_addr_start && pg_phy_addr < user_pool.phy_addr_start);

            // 线将对应的物理页框归还到内存池
            pfree(pg_phy_addr);

            // 再从页表中清除此虚拟地址所在的页表项 PTE
            page_table_pte_remove(vaddr);

            page_cnt++;
        }

        // 清空虚拟地址位图中的相应位
        vaddr_remove(pf, _vaddr, pg_cnt);
    }
}

// 回收内存 ptr
void sys_free(void* ptr) {
    ASSERT(ptr != NULL);
    if(ptr != NULL) {
        enum pool_flags PF;
        struct pool* mem_pool;

        // 判断线程还是进程
        if(running_thread() -> pgdir == NULL) { // 线程
            ASSERT((uint32_t) ptr >= K_HEAP_START);
            PF = PF_KERNEL;
            mem_pool = &kernel_pool;
        } else { // 进程
            PF = PF_USER;
            mem_pool = &user_pool;
        }

        lock_acquire(&mem_pool -> lock);

        struct mem_block* b = ptr; // 转为 mem_block 内存块类型指针
        struct arena* a = block2arena(b); // mem_block 转换到 arena

        ASSERT(a -> large == 0 || a -> large == 1);

        if(a -> desc == NULL && a -> large == true) // 要释放的内存大于 1024 字节
            mfree_page(PF, a, a -> cnt);
        else { // 要释放的内存小于或等于 1024 字节
            // 先将内存块回收到 free_list
            list_append(&a -> desc -> free_list, &b -> free_elem);

            // 再判断此 arena 中的内存块是否都是空闲块，若是，则释放 arena
            if(++a -> cnt == a -> desc -> blocks_per_arena) {
                uint32_t block_idx;
                for(block_idx = 0; block_idx < a -> desc -> blocks_per_arena; block_idx++) {
                    struct mem_block* b = arena2block(a, block_idx);
                    ASSERT(elem_find(&a -> desc -> free_list, &b -> free_elem));
                    list_remove(&b -> free_elem);
                }
                mfree_page(PF, a, 1); // 释放该 arena
            }
        }

        lock_release(&mem_pool -> lock);
    }
}

// 为 malloc 做准备
void block_desc_init(struct mem_block_desc* desc_array) {
    uint16_t desc_idx, block_size = 16;

    for(desc_idx = 0; desc_idx < DESC_CNT; desc_idx++) {
        desc_array[desc_idx].block_size = block_size;
        // 初始化 arena 中的内存块数量
        desc_array[desc_idx].blocks_per_arena = (PG_SIZE - sizeof(struct arena)) / block_size;
        
        list_init(&desc_array[desc_idx].free_list);

        block_size *= 2; // 更新为下一个规格的内存块
    }

}

// 初始化内存池
static void mem_pool_init(uint32_t all_mem) {
    put_str("  mem_pool_init start\n");
    uint32_t page_table_size = PG_SIZE * 256;       // 页表大小
    uint32_t used_mem = page_table_size + 0x100000; // 已使用的内存，0x100000 位低端1M
    uint32_t free_mem = all_mem - used_mem;         // 剩余的可用内存
    uint16_t all_free_pages = free_mem / PG_SIZE;   // 剩余的空闲内存可以分配成多少页

    // 计算内核和用户内存池可以分配到的物理容量，这里采用对半分
    uint16_t kernel_free_pages = all_free_pages / 2;
    uint16_t user_free_pages = all_free_pages - kernel_free_pages;

    // 因为位图每位表示 4k 即一页，而 1byte = 8bit，所以这里除 8 求得位图长度
    uint32_t kbm_length = kernel_free_pages / 8;
    uint32_t ubm_length = user_free_pages / 8;
    
    // 计算内核和用户内存池的起始地址
    uint32_t kp_start = used_mem; // Kernel Pool Start 内核内存池的起始地址
    uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; // User Pool Start 用户内存池的起始地址

    /* ----------------- 下面都是内核和用户内存池的初始化工作 --------------- */
    kernel_pool.phy_addr_start = kp_start;
    user_pool.phy_addr_start = up_start;

    kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
    user_pool.pool_size = user_free_pages * PG_SIZE;

    kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
    user_pool.pool_bitmap.btmp_bytes_len = ubm_length;

    /*位图是全局数据，长度不固定。
      全局或静态的数组需要在编译时直到其长度
      而我们需要根据总内存大小算出需要多少字节
      所以改为指定一块内存来生成位图*/
    kernel_pool.pool_bitmap.bits = (void*) MEM_BITMAP_BASE;
    // 用户内存池的位图紧跟在内核内存池的位图之后
    user_pool.pool_bitmap.bits = (void*) (MEM_BITMAP_BASE + kbm_length);

    /* ------------------------- 输出内存池信息 ---------------------------- */
    put_str("      kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits);
    put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start);
    put_str("\n");
    put_str("      user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits);
    put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start);
    put_str("\n");

    // 将位图置为 0
    bitmap_init(&kernel_pool.pool_bitmap);
    bitmap_init(&user_pool.pool_bitmap);

    // 初始化锁
    lock_init(&kernel_pool.lock);
    lock_init(&user_pool.lock);

    /* ---------- 下面初始化内核虚拟地址的位图，按实际物理内存大小生成数组 ---------- */
    // 用于维护内核堆的虚拟地址，所以要和内核内存池大小一致
    kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;

    // 位图的数组指向一块未使用的内存，目前定位在内核内存池和用户内存池之外
    kernel_vaddr.vaddr_bitmap.bits = (void*) (MEM_BITMAP_BASE + kbm_length + ubm_length);

    kernel_vaddr.vaddr_start = K_HEAP_START;
    bitmap_init(&kernel_vaddr.vaddr_bitmap);
    put_str("   mem_pool_init done\n");
}

// 内存管理部分的初始化入口
void mem_init() {
    put_str("mem_init start\n");

    // 0xb00 是 loader.S 中定义的 mem_bytes_total 存储总内存容量
    uint32_t mem_bytes_total = (*(uint32_t*)(0xb00)); 

    mem_pool_init(mem_bytes_total);	  // 初始化内存池
    block_desc_init(k_block_descs);
    put_str("mem_init done\n");
}
